/*************************************************************************/
/** Copyright.															**/
/** FileName: wxLCDBase.cpp												**/
/** Author: Polarix														**/
/** Description: LCD display panel in wxWidgets frame.					**/
/*************************************************************************/
//=======================================================================//
//= Include files.													    =//
//=======================================================================//
#include <wx/msgdlg.h>
#include <wx/clipbrd.h>
#include "Debug.h"
#include "wxLCDBase.h"

//=======================================================================//
//= Marco declare.                                                      =//
//=======================================================================//

//=======================================================================//
//= Global variable define.											    =//
//=======================================================================//
const wxSize	wxDefaultSizeInPixel(WX_LCD_DEFAULT_WIDTH_PIX, WX_LCD_DEFAULT_HEIGHT_PIX);

//=======================================================================//
//= Event table.													    =//
//=======================================================================//
BEGIN_EVENT_TABLE(wxLCDBase,wxWindow)
	EVT_PAINT		        (wxLCDBase::OnPaint)
	EVT_ERASE_BACKGROUND    (wxLCDBase::OnEraseBackGround)
	EVT_KEY_DOWN			(wxLCDBase::OnKeyDown)
	EVT_SET_FOCUS			(wxLCDBase::OnSetFocus)
END_EVENT_TABLE()

//=======================================================================//
//= Function define.										            =//
//=======================================================================//

wxLCDBuffer::wxLCDBuffer(const wxSize& clsSizeInPixel)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
    m_clsBufferSize = clsSizeInPixel;
    m_ppuiDisplayBuffer = _createNewDisplayBuffer(clsSizeInPixel.GetWidth(), clsSizeInPixel.GetHeight());
}

wxLCDBuffer::~wxLCDBuffer(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != m_ppuiDisplayBuffer)
	{
        _freeDisplayBuffer(m_ppuiDisplayBuffer);
	}
}

/*************************************************************************/
/** Function Name:	_createNewDisplayBuffer                             **/
/** Purpose:		Create new display buffer in heap.                  **/
/** Params:																**/
/**	@ iWidth[in]:   Width of display area buffer, i.e. horizontal pixel **/
/**                 number yet.                                         **/
/**	@ iHeight[in]:  Height of display area buffer, i.e. vertical pixel  **/
/**                 number yet.                                         **/
/** Return:			None.                                               **/
/** Notice:			This function will allocate buffer memory in heap   **/
/**                 and return the point after finished. need to be     **/
/**                 manually released after use ending.                 **/
/*************************************************************************/
unsigned int** wxLCDBuffer::_createNewDisplayBuffer(int iWidth, int iHeight)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	unsigned int**			ppuiNewDisplayBuffer;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	ppuiNewDisplayBuffer =  nullptr;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/

	if((iWidth > 0) && (iHeight > 0))
    {
        ppuiNewDisplayBuffer = (unsigned int**)malloc(sizeof(unsigned int*)*iHeight);
        if(nullptr != ppuiNewDisplayBuffer)
        {
            memset(ppuiNewDisplayBuffer, 0x00, sizeof(unsigned int*)*iHeight);
            for(int iIdxV=0; iIdxV<iHeight; iIdxV++)
            {
                unsigned int* puiNewRowBuffer = (unsigned int*)malloc(sizeof(unsigned int)*iWidth);
                if(nullptr != puiNewRowBuffer)
                {
                    memset(puiNewRowBuffer, 0x00, sizeof(unsigned int)*iWidth);
                    *(ppuiNewDisplayBuffer+iIdxV) = puiNewRowBuffer;
                }
                else
                {
                    _freeDisplayBuffer(ppuiNewDisplayBuffer);
                    ppuiNewDisplayBuffer = nullptr;
                    break;
                }
            }
        }
    }

    return ppuiNewDisplayBuffer;
}

void wxLCDBuffer::_cleanDisplayBuffer(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	for(int iIdxV=0; iIdxV<m_clsBufferSize.GetHeight(); iIdxV++)
    {
        memset(*(m_ppuiDisplayBuffer+iIdxV), 0x00, sizeof(unsigned int)*m_clsBufferSize.GetWidth());
    }
}

void wxLCDBuffer::_freeDisplayBuffer(unsigned int** ppuiDisplayBuffer)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != ppuiDisplayBuffer)
	{
	    for(int iIdxV=0; iIdxV<m_clsBufferSize.GetHeight(); iIdxV++)
        {
            free(*(ppuiDisplayBuffer+iIdxV));
        }
        free(ppuiDisplayBuffer);
	}
}

/*************************************************************************/
/** Function Name:	ResizeBuffer                                        **/
/** Purpose:		Resize the display buffer.                          **/
/** Params:																**/
/**	@ iWidth[in]:   Width of new display area buffer, i.e. horizontal   **/
/**                 pixel number yet.                                   **/
/**	@ iHeight[in]:  Height of new display area buffer, i.e. vertical    **/
/**                 pixel number yet.                                   **/
/** Return:			None.                                               **/
/** Notice:			This function will create a new display buffer and  **/
/**                 copy the visible data to new buffer, data out of    **/
/**                 visible range will be discarded.                    **/
/*************************************************************************/
void wxLCDBuffer::Resize(int iNewWidth, int iNewHeight)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	unsigned int**			ppuiNewDisplayBuffer = nullptr;
	int                		iCopiedRowNumber;
	int						iCopiedColumnNumber;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iNewWidth > 0) && (iNewHeight > 0))
    {
    	/* Only resize when size changed. */
    	if((iNewWidth != m_clsBufferSize.GetWidth()) || (iNewHeight != m_clsBufferSize.GetHeight()))
		{
			// Create a new display buffer
			ppuiNewDisplayBuffer = _createNewDisplayBuffer(iNewWidth, iNewHeight);
			if(nullptr != ppuiNewDisplayBuffer)
			{
				iCopiedRowNumber = m_clsBufferSize.GetHeight()<iNewHeight?m_clsBufferSize.GetHeight():iNewHeight;
				iCopiedColumnNumber = m_clsBufferSize.GetWidth()<iNewWidth?m_clsBufferSize.GetWidth():iNewWidth;
				// Copy old buffer content to new buffer;
				for(int iIdxV=0; iIdxV<iCopiedRowNumber; iIdxV++)
				{
					memcpy(*(ppuiNewDisplayBuffer+iIdxV), *(m_ppuiDisplayBuffer+iIdxV), sizeof(unsigned int)*iCopiedColumnNumber);
				}
				// Free current buffer.
				_freeDisplayBuffer(m_ppuiDisplayBuffer);
				m_ppuiDisplayBuffer = ppuiNewDisplayBuffer;
				DBG_LOG(wxT("Pixel buffer size updated, width:%d->%d, height:%d->%d."), m_clsBufferSize.GetWidth(), iNewWidth, m_clsBufferSize.GetHeight(), iNewHeight);
				m_clsBufferSize.Set(iNewWidth, iNewHeight);
			}
		}
		else
		{
			DBG_LOG(wxT("Pixel buffer size(w:%d, h:%d) is not changed."), iNewWidth, iNewHeight);
		}
    }
}

void wxLCDBuffer::Resize(const wxSize& clsNewSize)
{
    Resize(clsNewSize.GetWidth(), clsNewSize.GetHeight());
}

void wxLCDBuffer::CopyForm(const wxLCDBuffer& clsSourceBuffer)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	int                		iCopiedRowNumber;
	int						iCopiedColumnNumber;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	iCopiedRowNumber = m_clsBufferSize.GetHeight()<clsSourceBuffer.GetBufferSize().GetHeight()?m_clsBufferSize.GetHeight():clsSourceBuffer.GetBufferSize().GetHeight();
	iCopiedColumnNumber = m_clsBufferSize.GetWidth()<clsSourceBuffer.GetBufferSize().GetWidth()?m_clsBufferSize.GetWidth():clsSourceBuffer.GetBufferSize().GetWidth();

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	// Copy old buffer content to new buffer;
	for(int iIdxV=0; iIdxV<iCopiedRowNumber; iIdxV++)
	{
		memcpy(*(m_ppuiDisplayBuffer+iIdxV), *(clsSourceBuffer.GetData()+iIdxV), sizeof(unsigned int)*iCopiedColumnNumber);
	}
}

void wxLCDBuffer::SetData(int iPosX, int iPosY, unsigned int uiARGB)
{
	if((iPosX < m_clsBufferSize.GetWidth()) && (iPosY < m_clsBufferSize.GetHeight()))
	{
		*(*(m_ppuiDisplayBuffer+iPosY)+iPosX) = uiARGB;
	}
}

unsigned int wxLCDBuffer::GetData(int iPosX, int iPosY)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	uint32_t		uiReturnValue = 0x00;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iPosX < m_clsBufferSize.GetWidth()) && (iPosY < m_clsBufferSize.GetHeight()))
	{
		if(nullptr != m_ppuiDisplayBuffer)
		{
			uiReturnValue = *(*(m_ppuiDisplayBuffer+iPosY)+iPosX);
		}
	}

	return uiReturnValue;
}

/// wxLCDBase ///
wxLCDBase::wxLCDBase(wxWindow *pclsParent, wxWindowID iWinID, const wxPoint& clsPosition, const wxSize& clsSizeInPixel, const wxString& cstrName)
: wxWindow(pclsParent, iWinID, clsPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER, cstrName)
, m_clsCDC(this)
, m_clsDisplayBuffer(clsSizeInPixel)
{
	/*----------------------------------*/
	/* Initialize member.				*/
	/*----------------------------------*/
	m_pfDrawPoint = nullptr;
	// Initialize panel size.
	m_clsSizeInPixel = clsSizeInPixel;
	// Initialize paint buffer and function pointer.
	//m_pclsDisplayBuffer = new wxLCDBuffer(m_clsSizeInPixel);
	// Set pixel size.
	SetPixelUnitSize(wxDefaultLCDPixelUnitSize);
	// Set grid visible.
	SetGridVisibled(WX_LCD_DEFAULT_GRID_VISIBLE);
	// Set grid color.
	SetBorderWidth(WX_LCD_BORDER_WIDTH);
}

wxLCDBase::~wxLCDBase()
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

void wxLCDBase::_getBestSize(wxSize& clsBestSize) const
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool					bGridIsVisible;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	bGridIsVisible =		GetGridVisibled();

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	// Set size object value.
	clsBestSize.SetWidth(m_clsSizeInPixel.GetWidth()*m_clsPixelUnitSize.GetWidth()+(bGridIsVisible?1:0)+(2*m_iBorderWidth));
	clsBestSize.SetHeight(m_clsSizeInPixel.GetHeight()*m_clsPixelUnitSize.GetHeight()+(bGridIsVisible?1:0)+(2*m_iBorderWidth));
    DBG_LOG(wxT("<%s>New best size: w-%3d h-%3d"), GetName(), clsBestSize.GetWidth(), clsBestSize.GetHeight());
}

void wxLCDBase::_setMinClientSize(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxSize					clsNewMinSize;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	_getBestSize(clsNewMinSize);
	SetMinClientSize(clsNewMinSize);
}

void wxLCDBase::SetBorderWidth(int iBorderWidth)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_iBorderWidth = iBorderWidth;
}

void wxLCDBase::SetHorizontalPixelNumber(int iHorizontalPixelNumber)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(iHorizontalPixelNumber > 0)
    {
		_enterPaintCriticalSection();
		m_clsSizeInPixel.SetWidth(iHorizontalPixelNumber);
		m_clsDisplayBuffer.Resize(m_clsSizeInPixel);
		_setMinClientSize();
		_leavePaintCriticalSection();
    }
}

void wxLCDBase::SetVerticalPixelNumber(int iVerticalPixelNumber)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(iVerticalPixelNumber > 0)
    {
		_enterPaintCriticalSection();
		m_clsSizeInPixel.SetHeight(iVerticalPixelNumber);
		m_clsDisplayBuffer.Resize(m_clsSizeInPixel);
		_setMinClientSize();
		_leavePaintCriticalSection();
    }
}

/*************************************************************************/
/** Function Name:	SetPixelNumber                                      **/
/** Purpose:		Resize panel in pixel unit.                         **/
/** Params:																**/
/**	@ iHorizontalPixelNumber[in]: Width in pixel.                       **/
/**	@ iVerticalPixelNumber[in]: Height in pixel.                        **/
/** Return:			None.                                               **/
/** Notice:			This function will change the panel size in pixel   **/
/**                 unit number.                                        **/
/*************************************************************************/
void wxLCDBase::SetPixelNumber(int iHorizontalPixelNumber, int iVerticalPixelNumber)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iHorizontalPixelNumber > 0) && (iVerticalPixelNumber > 0) &&
		((m_clsSizeInPixel.GetWidth() != iHorizontalPixelNumber) || (m_clsSizeInPixel.GetHeight() != iVerticalPixelNumber)))
	{
		_enterPaintCriticalSection();
		m_clsSizeInPixel.Set(iHorizontalPixelNumber, iVerticalPixelNumber);
		m_clsDisplayBuffer.Resize(m_clsSizeInPixel);
		_setMinClientSize();
		_leavePaintCriticalSection();
	}
}

/*************************************************************************/
/** Function Name:	SetPixelNumber                                      **/
/** Purpose:		Resize panel in pixel unit.                         **/
/** Params:																**/
/**	@ clsNewSizeInPixelUnit[in]: New size in pixel unit.                **/
/** Return:			None.                                               **/
/** Notice:			This function will change the panel size in pixel   **/
/**                 unit number.                                        **/
/*************************************************************************/
void wxLCDBase::SetPixelNumber(const wxSize& clsNewSizeInPixelUnit)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	SetPixelNumber(clsNewSizeInPixelUnit.GetWidth(), clsNewSizeInPixelUnit.GetHeight());
}

int wxLCDBase::GetHorizontalPixelNumber(void) const
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return m_clsDisplayBuffer.GetBufferSize().GetWidth();
}


int wxLCDBase::GetVerticalPixelNumber(void) const
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return m_clsDisplayBuffer.GetBufferSize().GetHeight();
}

void wxLCDBase::GetPixelNumber(int* piHorizontalPixelNumber, int* piVerticalPixelNumber)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(NULL != piHorizontalPixelNumber)
	{
		*piHorizontalPixelNumber = m_clsSizeInPixel.GetWidth();
	}

	if(NULL != piVerticalPixelNumber)
	{
		*piVerticalPixelNumber = m_clsSizeInPixel.GetHeight();
	}
}

/*************************************************************************/
/** Function Name:	SetPixelUnitSize                                    **/
/** Purpose:		Set pixel unit size.                                **/
/** Params:																**/
/**	@ clsNewSizeInPixelUnit[in]: New size in pixel unit.                **/
/** Return:			None.                                               **/
/** Notice:			This function will change the panel size in pixel   **/
/**                 unit number.                                        **/
/*************************************************************************/
void wxLCDBase::SetPixelUnitSize(const wxSize& clsPixelUnitSize)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	// Save the pixel size value.
    m_clsPixelUnitSize = clsPixelUnitSize;

    _enterPaintCriticalSection();
    // If pixel size is 0, the pixel drawing function will set to invalid.
	if((0 == m_clsPixelUnitSize.GetWidth()) || (0 == m_clsPixelUnitSize.GetHeight()))
	{
		m_pfDrawPoint = NULL;
		m_bIsOK = false;
	}
	// If pixel size is 1, the pixel drawing function will set to draw pixel unit by point.
	/*
	else if(1 == m_iPixelSize)
	{
		m_pfDrawPoint = &_drawPointSinglePixel;
	}
	*/
	else
	{
		if(true == GetGridVisibled())
		{
			m_pfDrawPoint = &_drawPointMultiplePixelWithGrid;
		}
		else
        {
            m_pfDrawPoint = &_drawPointMultiplePixel;
        }
	}
	_leavePaintCriticalSection();
}

void wxLCDBase::SetPixelUnitSize(int iPixelUnitWidth, int iPixelUnitHeight)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	// Save the pixel size value.
    m_clsPixelUnitSize.Set(iPixelUnitWidth, iPixelUnitHeight);
	// Lock critical section.
    _enterPaintCriticalSection();
    // If pixel size is 0, the pixel drawing function will set to invalid.
	if((0 == m_clsPixelUnitSize.GetWidth()) || (0 == m_clsPixelUnitSize.GetHeight()))
	{
		m_pfDrawPoint = NULL;
		m_bIsOK = false;
	}
	// If pixel size is 1, the pixel drawing function will set to draw pixel unit by point.
	/*
	else if(1 == m_iPixelSize)
	{
		m_pfDrawPoint = &_drawPointSinglePixel;
	}
	*/
	else
	{
		if(true == GetGridVisibled())
		{
			m_pfDrawPoint = &_drawPointMultiplePixelWithGrid;
		}
		else
        {
            m_pfDrawPoint = &_drawPointMultiplePixel;
        }
	}
	_leavePaintCriticalSection();
}

void wxLCDBase::SetGridVisibled(bool bGridVisible)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	// Save the grid visible value set.
    if((m_clsPixelUnitSize.GetWidth() < WX_LCD_PIX_SIZE_MIN_WITH_GRID) || (m_clsPixelUnitSize.GetHeight() < WX_LCD_PIX_SIZE_MIN_WITH_GRID))
	{
		m_bGridVisible = false;
	}
	else
	{
		m_bGridVisible = bGridVisible;
	}
    _enterPaintCriticalSection();

    if(true == m_bGridVisible)
	{
		// Pixel unit size must large then WX_LCD_PIX_SIZE_MIN_WITH_GRID.
		m_pfDrawPoint = &_drawPointMultiplePixelWithGrid;
	}
	else
	{
		if((1 == m_clsPixelUnitSize.GetWidth()) && (1 == m_clsPixelUnitSize.GetHeight()))
		{
			m_pfDrawPoint = &_drawPointSinglePixel;
		}
		else
		{
			m_pfDrawPoint = &_drawPointMultiplePixel;
		}
	}
	_leavePaintCriticalSection();
}

bool wxLCDBase::GetGridVisibled(void) const
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool				bGridVisible;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((m_clsPixelUnitSize.GetWidth() < WX_LCD_PIX_SIZE_MIN_WITH_GRID) || (m_clsPixelUnitSize.GetHeight() < WX_LCD_PIX_SIZE_MIN_WITH_GRID))
	{
		bGridVisible = false;
	}
	else
	{
		bGridVisible = m_bGridVisible;
	}

	return bGridVisible;
}

void wxLCDBase::_cleanDisplayBuffer(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_clsDisplayBuffer.Clear();
	RefreshDisplay();
}

void wxLCDBase::FillColour(const wxColour& clsPanelColour)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((NULL != m_pfDrawPoint) && (m_clsPixelUnitSize.GetWidth() > 0) && (m_clsPixelUnitSize.GetHeight()))
	{
		_enterPaintCriticalSection();

		// Cleanup display and set display buffer .
		for(int iIdxV=0; iIdxV<m_clsSizeInPixel.GetHeight(); iIdxV++)
		{
			for(int iIdxH=0; iIdxH<m_clsSizeInPixel.GetWidth(); iIdxH++)
			{
				m_clsDisplayBuffer.SetData(iIdxH, iIdxV, clsPanelColour.GetRGBA());
			}
		}

		_leavePaintCriticalSection();
	}
}

void wxLCDBase::ReplaceColour(const wxColour& clsOldColour, const wxColour& clsNewColour)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((nullptr != m_pfDrawPoint) && (m_clsPixelUnitSize.GetWidth() > 0) && (m_clsPixelUnitSize.GetHeight()))
	{
		_enterPaintCriticalSection();

		// Cleanup display and set display buffer .
		for(int iIdxV=0; iIdxV<m_clsSizeInPixel.GetHeight(); iIdxV++)
		{
			for(int iIdxH=0; iIdxH<m_clsSizeInPixel.GetWidth(); iIdxH++)
			{
				if(m_clsDisplayBuffer.GetData(iIdxH, iIdxV) == clsOldColour.GetRGBA())
				{
					m_clsDisplayBuffer.SetData(iIdxH, iIdxV, clsNewColour.GetRGBA());
				}
			}
		}

		_leavePaintCriticalSection();
	}
}

void wxLCDBase::SetGridColor(const wxColor& clsColor)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_clsGridColor = clsColor;
}

wxColor& wxLCDBase::GetGridColor(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
    return m_clsGridColor;
}

void wxLCDBase::OnPaint(wxPaintEvent &clsEvent)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	RefreshDisplay();
	clsEvent.Skip();
}

void wxLCDBase::OnKeyDown(wxKeyEvent& clsEvent)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	clsEvent.ResumePropagation(1);
	clsEvent.Skip();
}

void wxLCDBase::OnSetFocus(wxFocusEvent& clsEvent)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	GetParent()->SetFocus();
	clsEvent.Skip();
}
void wxLCDBase::_drawPointSinglePixel(wxDC& clsDCObject, int iPosX, int iPosY, const wxSize& clsPixelSize)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	clsDCObject.DrawPoint(wxPoint(iPosX+m_iBorderWidth, iPosY+m_iBorderWidth));
}

void wxLCDBase::_drawPointMultiplePixel(wxDC& clsDCObject, int iPosX, int iPosY, const wxSize& clsPixelSize)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	clsDCObject.DrawRectangle(wxPoint(iPosX*m_clsPixelUnitSize.GetWidth()+m_iBorderWidth, iPosY*m_clsPixelUnitSize.GetHeight()+m_iBorderWidth), clsPixelSize);
}

void wxLCDBase::_drawPointMultiplePixelWithGrid(wxDC& clsDCObject, int iPosX, int iPosY, const wxSize& clsPixelSize)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	clsDCObject.DrawRectangle(wxPoint(iPosX*m_clsPixelUnitSize.GetWidth()+m_iBorderWidth+1, iPosY*m_clsPixelUnitSize.GetHeight()+m_iBorderWidth+1), wxSize(clsPixelSize.GetWidth()-1, clsPixelSize.GetHeight()-1));
}

/*************************************************************************/
/** Function Name:	SetPixelUnitColor                                   **/
/** Purpose:		Set a pixel RGBA color value.                       **/
/** Params:																**/
/**	@ iPosX[in]:		X-Coordinate of pixel.                          **/
/**	@ iPosY[in]:		Y-Coordinate of pixel.                          **/
/**	@ clsColor[in]:     Color data object.                              **/
/**	@ bRefreshNow[in]:  Refresh display at once, default to false.      **/
/** Return:			None.                                               **/
/** Notice:			This function only change the pixel color register  **/
/**                 if bRefreshNow is false(default), and when the      **/
/**                 parameter bRefreshNow is true, all pixel blocks of  **/
/**                 LCD screen panel will be repaint. if need to draw   **/
/**                 only one pixel, please use the DrawPixel function   **/
/**                 directly.                                           **/
/*************************************************************************/
void wxLCDBase::SetPixelUnitColor(int iPosX, int iPosY, const wxColor& clsColor)
{
    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iPosX < m_clsSizeInPixel.GetWidth()) && (iPosY < m_clsSizeInPixel.GetHeight()))
	{
		m_clsDisplayBuffer.SetData(iPosX, iPosY, clsColor.GetRGBA());
	}
}

/*************************************************************************/
/** Function Name:	GetPixelUnitColor                                   **/
/** Purpose:		Get color of a pixel unit.                          **/
/** Params:																**/
/**	@ uiPosX[in]:       X-Coordinate of pixel.                          **/
/**	@ uiPosY[in]:       Y-Coordinate of pixel.                          **/
/** Return:			RGBA color value of the pixel unit.                 **/
/** Notice:			None.                                               **/
/*************************************************************************/
unsigned int wxLCDBase::GetPixelUnitColor(int iPosX, int iPosY)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	uint32_t		uiReturnValue = 0x00;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iPosX < m_clsSizeInPixel.GetWidth()) && (iPosY < m_clsSizeInPixel.GetHeight()))
	{
		uiReturnValue = m_clsDisplayBuffer.GetData(iPosX, iPosY);
	}

	return uiReturnValue;
}

/*************************************************************************/
/** Function Name:	DrawPixel                                           **/
/** Purpose:		Draw a pixel.                                       **/
/** Params:																**/
/**	@ iPosX[in]:		X-Coordinate of pixel.                          **/
/**	@ iPosY[in]:		Y-Coordinate of pixel.                          **/
/**	@ clsColor[in]:     Color data object.                              **/
/** Return:			None.                                               **/
/** Notice:			Draw only one pixel directly.                       **/
/*************************************************************************/
void wxLCDBase::DrawPixel(int iPosX, int iPosY, const wxColor& clsColor)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iPosX < m_clsSizeInPixel.GetWidth()) && (iPosY < m_clsSizeInPixel.GetHeight()) && (m_clsPixelUnitSize.GetWidth() != 0) && (m_clsPixelUnitSize.GetHeight() != 0))
	{
		_enterPaintCriticalSection();

		_setDCColor(clsColor);
		_prepareDC(m_clsCDC);
		if(nullptr != m_pfDrawPoint)
		{
			(this->*m_pfDrawPoint)(m_clsCDC, iPosX, iPosY, m_clsPixelUnitSize);
		}
		m_clsDisplayBuffer.SetData(iPosX, iPosY, clsColor.GetRGBA());
		SetPixelUnitColor(iPosX, iPosY, clsColor);
		_releaseDC(m_clsCDC);

		_leavePaintCriticalSection();
	}
}

/*************************************************************************/
/** Function Name:	FillRect                                            **/
/** Purpose:		Fill a rectangle area with the specified color.     **/
/** Params:																**/
/**	@ iPosX[in]:    X-Coordinate in pixel unit.                         **/
/**	@ iPosY[in]:    Y-Coordinate in pixel unit.                         **/
/**	@ iWidthPix[in]: Width in pixel unit.                               **/
/**	@ iHeightPix[in]: Height in pixel unit.                             **/
/**	@ clsColour[in]: Fill color.                                        **/
/** Return:			None.                                               **/
/*************************************************************************/
void wxLCDBase::FillRect(int iPosX, int iPosY, int iWidthPix, int iHeightPix, const wxColour& clsColour)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	int					iFillWidth = m_clsDisplayBuffer.GetBufferSize().GetWidth()-iPosX;
	int					iFillHeight = m_clsDisplayBuffer.GetBufferSize().GetHeight()-iPosY;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	if(iFillWidth > iWidthPix)
	{
		iFillWidth = iWidthPix;
	}
	if(iFillHeight > iHeightPix)
	{
		iFillHeight = iHeightPix;
	}

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	for(int iFillIdxY=0; iFillIdxY<iFillHeight; iFillIdxY++)
	{
		for(int iFillIdxX=0; iFillIdxX<iFillWidth; iFillIdxX++)
		{
			m_clsDisplayBuffer.SetData(iFillIdxX+iPosX, iFillIdxY+iPosY, clsColour.GetRGBA());
		}
	}
}

/*************************************************************************/
/** Function Name:	RefreshDisplay                                      **/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		Repaint LCD screen panel.                           **/
/** Params:			None.                                               **/
/** Return:			None.                                               **/
/** Notice:			Call this function after the SetPixelColor called,  **/
/**                 all pixels of the LCD panel will be repaint by the  **/
/**                 pixels's RGBA value register array.                 **/
/*************************************************************************/
void wxLCDBase::RefreshDisplay(void)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool				bGridVisible;
	int					iPaintSizeWidth, iPaintSizeHeight;
	unsigned int		uiColorRGBA;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	iPaintSizeWidth =	m_clsSizeInPixel.GetWidth()*m_clsPixelUnitSize.GetWidth()+(2*m_iBorderWidth);
	iPaintSizeHeight =	m_clsSizeInPixel.GetHeight()*m_clsPixelUnitSize.GetHeight()+(2*m_iBorderWidth);
	bGridVisible = GetGridVisibled();

	// Set buffer size.
	if(true == bGridVisible)
	{
		iPaintSizeWidth++;
		iPaintSizeHeight++;
	}

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if((iPaintSizeWidth > 0) && (iPaintSizeHeight > 0))
	{
		wxBitmap			clsPaintBitMap;
		wxBufferedDC		clsBufferedDC;

		// Create buffer image and DC object.
		clsPaintBitMap.Create(iPaintSizeWidth, iPaintSizeHeight);
		clsBufferedDC.Init(&m_clsCDC, clsPaintBitMap);

		_enterPaintCriticalSection();
		// Clear background for grid line.
		if(true == bGridVisible)
		{
			_setDCColor(m_clsGridColor);
			_prepareDC(clsBufferedDC);
			clsBufferedDC.DrawRectangle(wxPoint(m_iBorderWidth, m_iBorderWidth),
								wxSize(	m_clsSizeInPixel.GetWidth()*m_clsPixelUnitSize.GetWidth()+1,
										m_clsSizeInPixel.GetHeight()*m_clsPixelUnitSize.GetHeight()+1));
		}
		// Paint pixel.
		if(nullptr != m_pfDrawPoint)
		{
			// Refresh each pixel on screen.
			for(int i_H=0; i_H<m_clsSizeInPixel.GetHeight(); i_H++)
			{
				for(int i_W=0; i_W<m_clsSizeInPixel.GetWidth(); i_W++)
				{
					uiColorRGBA = m_clsDisplayBuffer.GetData(i_W, i_H);
					_setDCColor(wxColor(uiColorRGBA));
					_prepareDC(clsBufferedDC);
					(this->*m_pfDrawPoint)(clsBufferedDC, i_W, i_H, m_clsPixelUnitSize);
				}
			}
		}

		_leavePaintCriticalSection();
	}
}

/*************************************************************************/
/** Function Name:	_getLCDPanelImage									**/
/** Class:			wxLCDBase											**/
/** Accessibility:	Private.											**/
/** Purpose:		Copy LCD panel image to bitmap object.				**/
/** Resources:		None.												**/
/** Params:																**/
/** @ clsBitmap[in/out]: Out put bitmap object.							**/
/** Return:																**/
/** @ true:				Copy image successfully.						**/
/**	@ false:			Operation failed.								**/
/** Notice:			Size and depth of Bitmap object must set before		**/
/**					this function called.								**/
/*************************************************************************/
bool wxLCDBase::_screenShot(wxBitmap& clsBitmap)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxMemoryDC				clsMemoryDC(clsBitmap);
	bool                    bReturn;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	bReturn =               false;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	bReturn = clsMemoryDC.Blit(wxPoint(0, 0), GetSize(), &m_clsCDC, wxPoint(0, 0));
	return bReturn;
}

/*************************************************************************/
/** Function Name:	SaveScreenImageToFile                               **/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		Save current panel image to a jpeg file.            **/
/** Params:			                                                    **/
/**	@ strFilePath[in]:  Save file path.                                 **/
/** Return:                                                             **/
/** @ true:             Save successfully.                              **/
/** @ false:            Save failed.                                    **/
/** Notice:			To support jpeg format, need add wxWidgets jpeg     **/
/**                 format handler in initialize(OnInit) function of    **/
/**                 the wxApp Object.                                   **/
/**                 wxImage::AddHandler(new wxJPEGHandler)              **/
/*************************************************************************/
bool wxLCDBase::SaveScreenshot(const wxString& strFilePath)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxBitmap				clsBitMap(GetSize(), wxBITMAP_SCREEN_DEPTH);
	bool                    bReturn;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	bReturn =               false;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	bReturn = _screenShot(clsBitMap);
	if(true == bReturn)
	{
		bReturn = clsBitMap.SaveFile(strFilePath, wxBITMAP_TYPE_JPEG);
	}

	return bReturn;
}

/*************************************************************************/
/** Function Name:	CopyScreenImageToClipBoard                          **/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		Copy current screen image to clip board.            **/
/** Params:			None.                                               **/
/** Return:                                                             **/
/** @ true:             Copy successfully.                              **/
/** @ false:            Copy failed.                                    **/
/** Notice:			None.                                               **/
/*************************************************************************/
bool wxLCDBase::CopyScreenshot(void)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxBitmap				clsBitMap(GetSize(), wxBITMAP_SCREEN_DEPTH);
	bool                    bReturn;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	bReturn =               true;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(true == wxTheClipboard->Open())
	{
		do
		{
			bReturn = _screenShot(clsBitMap);
			if(false == bReturn)
			{
                // Ger LCD panel image failed.
                break;
			}

			bReturn = wxClipboard::Get()->SetData(new wxBitmapDataObject(clsBitMap));
			if(false == bReturn)
			{
                // Set data to clipboard failed.
                break;
			}
			wxTheClipboard->Flush();
		}while(0);
		wxTheClipboard->Close();
	}
	else
	{
		bReturn = false;
	}
	return bReturn;
}

/*************************************************************************/
/** Function Name:	DoGetBestClientSize									**/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		Get best display size with current size in pixel	**/
/**					unit size.											**/
/** Resources:		None.												**/
/** Params:			None.												**/
/** Return:			Size object.										**/
/** Notice:			This function will be call when user call base		**/
/**					class function GetBestSize.							**/
/*************************************************************************/
wxSize wxLCDBase::DoGetBestClientSize(void) const
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxSize					clsBestSize;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	_getBestSize(clsBestSize);
	return clsBestSize;
}

/*************************************************************************/
/** Function Name:	DoGetBestSize										**/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		get the best size with current size in pixel unit	**/
/**					size for layout.									**/
/** Resources:		None.												**/
/** Params:			None.												**/
/** Return:			Size object.										**/
/** Notice:			None.												**/
/*************************************************************************/
wxSize wxLCDBase::DoGetBestSize(void) const
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxSize					clsBestSize;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	_getBestSize(clsBestSize);
	return clsBestSize;
}

/*************************************************************************/
/** Function Name:	FitBestSize 										**/
/** Class:			wxLCDBase											**/
/** Accessibility:	Public.												**/
/** Purpose:		Fit and set object to the best layout size.			**/
/** Resources:		None.												**/
/** Params:			None.												**/
/** Return:			None.       										**/
/** Notice:			None.												**/
/*************************************************************************/
void wxLCDBase::FitBestSize(void)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxSize					clsBestSize;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	_getBestSize(clsBestSize);
	SetSize(clsBestSize);
}
